package com.galeno.day07;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.api.java.tuple.Tuple4;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * @author galeno
 * @Title:
 * @Description:
 * @date 2021/10/2320:07
 */
public class AdCount {
    public static void main(String[] args) throws Exception {
        Configuration configuration = new Configuration();
        configuration.setInteger("rest.port", 22222);
        StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(configuration);
        env.enableCheckpointing(10000);
        DataStreamSource<String> lines = env.socketTextStream("galeno", 9999);
        SingleOutputStreamOperator<Tuple3<String, String, String>> maped = lines.map(x -> {
            String[] split = x.split(",");

            return Tuple3.of(split[0], split[1], split[2]);
        }).returns(new TypeHint<Tuple3<String, String, String>>() {
        });
        KeyedStream<Tuple3<String, String, String>, Tuple2<String, String>> keyedStream = maped.keyBy(new KeySelector<Tuple3<String, String, String>, Tuple2<String, String>>() {
            @Override
            public Tuple2<String, String> getKey(Tuple3<String, String, String> value) throws Exception {

                return Tuple2.of(value.f0, value.f2);
            }
        });
        SingleOutputStreamOperator<Tuple4<String, String, Integer, Integer>> res = keyedStream.map(new RichMapFunction<Tuple3<String, String, String>, Tuple4<String, String, Integer, Integer>>() {

            private ValueState<Integer> contState;
            private ValueState<Integer> uVCount;
            private ValueState<BloomFilter<String>> uidBloomState;

            @Override
            public void open(Configuration parameters) throws Exception {
                ValueStateDescriptor<Integer> countStateDescriptor = new ValueStateDescriptor<>("count-state", Integer.class);
                contState = getRuntimeContext().getState(countStateDescriptor);

                ValueStateDescriptor<BloomFilter<String>> bloomFilterValueStateDescriptor = new ValueStateDescriptor<>("bloom-state", TypeInformation.of(new TypeHint<BloomFilter<String>>() {
                }));
                uidBloomState = getRuntimeContext().getState(bloomFilterValueStateDescriptor);

                ValueStateDescriptor<Integer> integerValueStateDescriptor = new ValueStateDescriptor<>("UV-state", Integer.class);
                uVCount = getRuntimeContext().getState(integerValueStateDescriptor);

            }

            @Override
            public Tuple4<String, String, Integer, Integer> map(Tuple3<String, String, String> value) throws Exception {
                String aid = value.f0;
                String uid = value.f1;
                String eventType = value.f2;
                /**
                 * 统计次数
                 */
                Integer historyCount = contState.value();
                if (historyCount == null) {
                    historyCount = 0;
                }
                historyCount += 1;
                Integer uvHistoryCount = uVCount.value();
                if (uvHistoryCount == null) {
                    uvHistoryCount = 0;
                }
                BloomFilter<String> bloomFilter = uidBloomState.value();
                if (bloomFilter == null) {
                    bloomFilter = BloomFilter.create(Funnels.unencodedCharsFunnel(), 10000000, 0.001);
                }
                if (!bloomFilter.mightContain(uid)) {
                    uvHistoryCount += 1;
                    bloomFilter.put(uid);
                    uVCount.update(uvHistoryCount);
                    uidBloomState.update(bloomFilter);
                    return Tuple4.of(aid, eventType, historyCount, uvHistoryCount);
                }
                return Tuple4.of(aid, eventType, historyCount, uvHistoryCount);
            }
        });
        res.print();
        env.execute();


    }
}
